/*********************************************************************************
* Copyright (c) zhujinyang.
* All rights reserved.
*
* \file
*
* fifo implement, Multithread safety, Support multiple producers and consumers.
* Also supports interrupts
* 
*********************************************************************************/
#include "os_ext_api.h"
#include <string.h>
#include "cc_fifo.h"





/*
********************************************************************************
* define config parameters
********************************************************************************
*/

/*
********************************************************************************
* define struct
********************************************************************************
*/

/*
********************************************************************************
* define module variants
********************************************************************************
*/







/**********************************************************************************
* \brief:       create a fifo
* \param[in]:   fifo:    the pointer fifo
* \param[in]:   buf:     buffer pointer
* \param[in]:   size:    the buffer size
* \return:      none
**********************************************************************************/
void cc_fifo_create(CC_FIFO_T *fifo, INT8U *buf, INT32U size)
{
    fifo->size       = size;
    fifo->used_in    = 0;
    fifo->used_out   = 0;
    fifo->in         = 0;
    fifo->out        = 0;
    fifo->buf        = buf;
}


/**********************************************************************************
* \brief:       destory fifo
* \param[in]:   fifo:    the pointer fifo
* \return:      none
**********************************************************************************/
void cc_fifo_destory(CC_FIFO_T *fifo)
{
    os_enter_atomic_ext();
    fifo->size       = 0;
    fifo->used_in    = 0;
    fifo->used_out   = 0;
    fifo->in         = 0;
    fifo->out        = 0;
    fifo->buf        = 0;
    os_exit_atomic_ext();
}

/**********************************************************************************
* \brief:       write data to fifo
* \param[in]:   fifo:    the pointer fifo
* \param[in]:   data:    buffer pointer
* \param[in]:   dlen:    the buffer length
* \return:      success : Actual write length, 0: Insufficient cache
**********************************************************************************/
INT32U cc_fifo_writedata(CC_FIFO_T *fifo, INT8U *data, INT32U dlen)
{
    INT32U  in, len;

    os_enter_atomic_ext();
    if ((fifo->size - fifo->used_in) < dlen) {
        os_exit_atomic_ext();
        return 0;
    } else {
        in = fifo->in;
        fifo->in += dlen;
        if (fifo->in >= fifo->size) {
            fifo->in -= fifo->size;
        }
        fifo->used_in += dlen;
    }
    os_exit_atomic_ext();

    len = fifo->size - in;
    if (dlen > len) {
        memcpy(&fifo->buf[in], data,  len);
        memcpy(fifo->buf, &data[len], dlen - len);
    } else {
        memcpy(&fifo->buf[in], data, dlen);
    }
    
    os_enter_atomic_ext();
    fifo->used_out += dlen;
    os_exit_atomic_ext();

    return dlen;
}




/**********************************************************************************
* \brief:       wtite a byte to fifo
* \param[in]:   fifo:    the pointer fifo
* \param[in]:   byte:    write byte
* \return:      success : Actual write length, 0: Insufficient cache
**********************************************************************************/
INT32U cc_fifo_writebyte(CC_FIFO_T *fifo, INT8U byte)
{
    os_enter_atomic_ext();
    if (fifo->used_in >= fifo->size) {
        os_exit_atomic_ext();
        return 0;
    }
    fifo->buf[fifo->in] = byte;
    if (++fifo->in >= fifo->size) {
        fifo->in = 0;
    }
    
    fifo->used_in++;
    fifo->used_out++;
    os_exit_atomic_ext();

    return 1;
}


/**********************************************************************************
* \brief:       read data form fifo
* \param[in]:   fifo:    the pointer fifo
* \param[out]:  data:    receive buffer pointer
* \param[in]:   dlen:    receive buffer length
* \return:      success : Actual reception length, 0: no reception data
**********************************************************************************/
INT32U cc_fifo_readdata(CC_FIFO_T *fifo, INT8U *data, INT32U dlen)
{
    INT32U  out, len;

    os_enter_atomic_ext();
    if (fifo->used_out == 0) {
        os_exit_atomic_ext();
        return 0;
    } else {
        out = fifo->out;
        if (dlen > fifo->used_out) {
            dlen = fifo->used_out;
        }
        fifo->out += dlen;
        if (fifo->out >= fifo->size) {
            fifo->out -= fifo->size;
        }
        fifo->used_out -= dlen;
    }
    os_exit_atomic_ext();

    len = fifo->size - out;
    if (dlen > len) {
        memcpy(data,  &fifo->buf[out], len);
        memcpy(&data[len], fifo->buf,  dlen - len);
    } else {
        memcpy(data,  &fifo->buf[out], dlen);
    }
    
    os_enter_atomic_ext();
    fifo->used_in  -= dlen;
    os_exit_atomic_ext();

    return dlen;
}


/**********************************************************************************
* \brief:       read a byte form fifo
* \param[in]:   fifo:    the pointer fifo
* \return:      < 0: failure
**********************************************************************************/
INT32S cc_fifo_readbyte(CC_FIFO_T *fifo)
{
    INT8U byte;

    os_enter_atomic_ext();
    if (fifo->used_in == 0) {
        os_exit_atomic_ext();
        return -1;
    }
    byte = fifo->buf[fifo->out] ;
    if (++fifo->out >= fifo->size) {
        fifo->out = 0;
    }
    
    fifo->used_in--;
    fifo->used_out--;
    os_exit_atomic_ext();

    return byte;
}



/**********************************************************************************
* \brief:       return fifo left bytes
* \param[in]:   fifo:    the pointer fifo
* \param[in]:   producer_or_consumer:    0: producer, writer 1: consumer, reader
* \return:      the left bytes of fifo
**********************************************************************************/
INT32U cc_fifo_left(CC_FIFO_T *fifo,  INT32U producer_or_consumer)
{
    INT32U left;

    os_enter_atomic_ext();
    if (producer_or_consumer == 0) {
        left = (fifo->size - fifo->used_in);
    } else {
        left = (fifo->size - fifo->used_out);
    }
    os_exit_atomic_ext();

    return left;
}




/**********************************************************************************
* \brief:       return fifo used bytes
* \param[in]:   fifo:    the pointer fifo
* \param[in]:   producer_or_consumer:    0: producer, writer 1: consumer, reader
* \return:      the used bytes of fifo
**********************************************************************************/
INT32U cc_fifo_used(CC_FIFO_T *fifo,  INT32U producer_or_consumer)
{
    INT32U used;

    os_enter_atomic_ext();
    if (producer_or_consumer == 0) {
        used = fifo->used_in;
    } else {
        used = fifo->used_out;
    }
    os_exit_atomic_ext();

    return used;
}

/**********************************************************************************
* \brief:       return fifo size
* \param[in]:   fifo:    the pointer fifo
* \return:      the size of fifo
**********************************************************************************/
INT32U cc_fifo_size(CC_FIFO_T *fifo)
{
    INT32U size;

    os_enter_atomic_ext();
    size = fifo->size;
    os_exit_atomic_ext();
    
    return size;
}





